/*!
* 
* Copyright (c) 2009 by yuqiang @ vcedu
* 
* Description:
*		This file contains the video session
*
* ChangeLog:
*		$Date$	$Rev$	$Author$
* 
*/

/**
 * \file VideoSession.h
 */

#ifndef _VideoSession_H_
#define _VideoSession_H_

#include <mipcomponentchain.h>
#include <miperrorbase.h>
#include <miptime.h>
#include <rtptransmitter.h>
#include <string>

#include "videooutput.h"

class RTPSession;
class RTPAddress;
class MIPRTPSynchronizer;
class MIPDirectShowCapture;
class MIPAVCodecEncoder;
class MIPRTPVideoEncoder;
class MIPRTPComponent;
class MIPAverageTimer;
class MIPRTPDecoder;
class MIPRTPVideoDecoder;
class MIPMediaBuffer;
class MIPAVCodecDecoder;
class MIPVideoMixer;
class MIPAVCodecFrameConverter;

/** Parameters for a video session. */
class VideoSessionParams
{
public:
	VideoSessionParams()								
	{ 
		m_devNum = 0;

		m_width = 320;
		m_height = 240;
		m_frameRate = 25.0;
		m_portbase = 6100; 
		m_acceptOwnPackets = true;
		m_bandwidth = 200000;
	}
	~VideoSessionParams() { }

	/** Returns the device number to open (only available on Win32; default: 0). */
	int getDevice() const								{ return m_devNum; }

	/** Returns the width (default: 160). */
	int getWidth() const								{ return m_width; }
	
	/** Returns the height (default: 120). */
	int getHeight() const								{ return m_height; }

	/** Returns the frame rate (default: 15.0). */
	real_t getFrameRate() const							{ return m_frameRate; }

	/** Returns the RTP portbase (default: 5100). */
	uint16_t getPortbase() const							{ return m_portbase; }

	/** Returns \c true if own packets will be accepted (default: \c false). */
	bool getAcceptOwnPackets() const						{ return m_acceptOwnPackets; }

	/** Returns the bandwidth that can be used (default: 200000). */
	int getBandwidth() const							{ return m_bandwidth; }
	
	/** Sets the number of the input device to use (only available on Win32). */
	void setDevice(int n)								{ m_devNum = n; }

	/** Sets the width of the video frames. */
	void setWidth(int w)								{ m_width = w; }
	
	/** Sets the height of the video frames. */
	void setHeight(int h)								{ m_height = h; }

	/** Sets the bandwidth. */
	void setBandwidth(int b)							{ m_bandwidth = b; }

	/** Sets the frame rate. */
	void setFrameRate(real_t r)							{ m_frameRate = r; }

	/** Sets the RTP portbase. */
	void setPortbase(uint16_t p)							{ m_portbase = p; }
	
	/** Sets a flag indicating if own packets should be accepted. */
	void setAcceptOwnPackets(bool v)						{ m_acceptOwnPackets = v; }

private:

	int m_devNum;

	int m_width, m_height;
	real_t m_frameRate;
	uint16_t m_portbase;
	bool m_acceptOwnPackets;
	int m_bandwidth;
};

/** Creates a video over IP session.
 *  This wrapper class can be used to create a video over IP session. Transmitted data
 *  will be H.263+ encoded. Destinations are specified using subclasses of
 *  RTPAddress from the JRTPLIB library. Currently, only RTPIPv4Address
 *  instances can be specified.
 */
class VideoSession : public MIPErrorBase
{
public:
	VideoSession();
	virtual ~VideoSession();

	/** Initializes the session.
	 *  Using this function, the session is initialized.
	 *  \param pParams Session parameters.
	 *  \param pSync RTP stream synchronizer.
	 *  \param pRTPSession Supply your own RTPSession instance with this parameter. In this case,
	 *                     the RTPSession instance is not deleted when the video session is destroyed.
	 *                     The session has to be initialized, but the timestamp unit will still be 
	 *                     adjusted.
 	 */
	bool init(const VideoSessionParams *pParams = 0, MIPRTPSynchronizer *pSync = 0, RTPSession *pRTPSession = 0);
	
	/** Destroys the session. */
	bool destroy();

	/** Add a destination. */
	bool addDestination(const RTPAddress &addr);

	/** Delete a destination. */
	bool deleteDestination(const RTPAddress &addr);

	/** Clear the destination list. */
	bool clearDestinations();

	/** Returns \c true if multicasting is supported, \c false otherwise. */
	bool supportsMulticasting();

	/** Joins a multicast group. */
	bool joinMulticastGroup(const RTPAddress &addr);

	/** Leaves a multicast group. */
	bool leaveMulticastGroup(const RTPAddress &addr);

	/** Leaves all multicast groups. */
	bool leaveAllMulticastGroups();
	
	/** Set a receive mode.
	 *  Using this function, a receive mode can be specified. Valid receive modes are
	 *  \c RTPTransmitter::AcceptAll, \c RTPTransmitter::AcceptSome and \c RTPTransmitter::IgnoreSome.
	 *  In the last two cases, packets are accepted or ignored based upon information in the
	 *  accept or ignore list. Note that changing the receive mode will cause such a list to be cleared.
	 */
	bool setReceiveMode(RTPTransmitter::ReceiveMode m);

	/** Adds an address to the ignore list. */
	bool addToIgnoreList(const RTPAddress &addr);

	/** Removes an address from the ignore list. */
	bool deleteFromIgnoreList(const RTPAddress &addr);

	/** Clears the ignore list. */
	bool clearIgnoreList();

	/** Adds an address to the accept list. */
	bool addToAcceptList(const RTPAddress &addr);

	/** Deletes an address from the accept list. */
	bool deleteFromAcceptList(const RTPAddress &addr);

	/** Clears the accept list. */
	bool clearAcceptList();

	/** If the video frame storage component is being used, this function retrieves the
	 *  list of source IDs of which video frames are currently stored.
	 */
	bool getSourceIDs(std::list<uint64_t> &sourceIDs);

	/**
	 *  start send video chain, so user can receive our video
	 */
	bool startSendChain();

	/**
	*  stop send video chain, so user can not receive our video
	*/
	bool stopSendChain();

	/**
	*  start recieve video chain, so we can receive user's video
	*/
	bool startReceiveChain();

	/**
	*  stop recieve video chain, so we can not receive user' video
	*/
	bool stopReceiveChain();

	/**
	 *  start the video session
	 */
	bool startSession();

	/**
	*  stop the video session
	*/
	bool stopSession();

	bool isInit() {return m_init;}

	bool isSessionRunning() {return (m_inputRunning || m_outputRunning);}

	bool isSendRunning() {return m_outputRunning;}

	bool isReceiveRunning() {return m_inputRunning;}

protected:
	/** By overriding this function, you can detect when the input thread has finished.
	 *  By overriding this function, you can detect when the input thread has finished.
	 *  \param err Flag indicating if the thread stopped due to an error.
	 *  \param compName Contains the component in which the error occured.
	 *  \param errStr Contains a description of the error.
	 */
	virtual void onInputThreadExit(bool err, const std::string &compName, const std::string &errStr);

	/** By overriding this function, you can detect when the output thread has finished.
	 *  By overriding this function, you can detect when the output thread has finished.
	 *  \param err Flag indicating if the thread stopped due to an error.
	 *  \param compName Contains the component in which the error occured.
	 *  \param errStr Contains a description of the error.
	 */
	virtual void onOutputThreadExit(bool err, const std::string &compName, const std::string &errStr);
private:
	class InputChain : public MIPComponentChain
	{
	public:
		InputChain(VideoSession *pVideoSess) : MIPComponentChain("Input chain")		{ m_pVideoSess = pVideoSess; }
	protected:
		void onThreadExit(bool err, const std::string &compName, const std::string &errStr)	{ m_pVideoSess->onInputThreadExit(err, compName, errStr); }
	private:
		VideoSession *m_pVideoSess;
	};

	class OutputChain : public MIPComponentChain
	{
	public:
		OutputChain(VideoSession *pVideoSess) : MIPComponentChain("Output chain")		{ m_pVideoSess = pVideoSess; }
	protected:
		void onThreadExit(bool err, const std::string &compName, const std::string &errStr)	{ m_pVideoSess->onOutputThreadExit(err, compName, errStr); }
	private:
		VideoSession *m_pVideoSess;
	};

	void zeroAll();
	void deleteAll();

	bool m_init;
	bool m_openCamera;
	bool m_inputRunning;
	bool m_outputRunning;

	InputChain *m_pInputChain;
	OutputChain *m_pOutputChain;
	MIPAverageTimer *m_pTimer;

	MIPDirectShowCapture *m_pInput;

	MIPAVCodecFrameConverter *m_pInputFrameConverter;
	MIPAVCodecEncoder *m_pAvcEnc;
	MIPRTPVideoEncoder *m_pRTPEnc;
	MIPRTPComponent *m_pRTPComp;
	
	RTPSession *m_pRTPSession;
	bool m_deleteRTPSession;
	
	MIPAverageTimer *m_pTimer2;
	MIPRTPDecoder *m_pRTPDec;
	MIPRTPVideoDecoder *m_pRTPVideoDec;
	MIPMediaBuffer *m_pMediaBuf;
	MIPAVCodecDecoder *m_pAvcDec;
	MIPVideoMixer *m_pMixer;
	
	librtp_video::VideoOuput* m_pVideoOutput;

	friend class InputChain;
	friend class OutputChain;
};

#endif // _VideoSession_H_

